#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <list>
#include <unistd.h> //sleep(1)
#include <numeric>
#include <vector>
#include <future>

using namespace std;
using namespace this_thread;

class CppDemo
{
public:
    CppDemo();

    static void PrintThread(int a, int b)
    {
        std::cout<<"PrintThread:"<<a<<"--"<<b<<std::endl;
    }

    static void Start()
    {
        std::cout<<"Class Thread Start--detach:";
        std::thread thd(PrintThread,10,20);
        thd.detach();
    }
};

void FunThread()
{
   std::cout<<"FunThread thread executing--join."<<std::endl;
   std::this_thread::sleep_for(std::chrono::seconds(1));
}

//数据同步：线程同时操作一个数据的安全性
std::mutex mtx; //线程互斥对象:避免多个线程同时访问共享资源
int data = 1;  //这会避免数据竞争并提供线程间同步支持

void Add(int a)
{
    mtx.lock();//锁定指定的互斥体，若任何一个不可用则阻塞
    data+=a;
    std::cout<<"Add data = "<<data<<endl;
    mtx.unlock();
}

void Multi(int a)
{
    mtx.lock();
    data*=a;
    std::cout<<"Multi data = "<<data<<endl;
    mtx.unlock();
}

//线程同步：生产者-消费者模型
//std::condition_variable只可与 std::unique_lock<std::mutex>一同使用。
//默认情况下，std::unique_lock的构造函数会对mutex进行加锁，在析构的时候会对mutex进行解锁
//使用条件变量同步机制
std::condition_variable producervar, consumervar;

std::list<int> nList;//数据缓冲区
static const int MAXBUFF = 5;
static const int PRODUCEDATA = 20;

//生产者线程
void ProducerThread(int ndata)
{
    sleep(1);

    std::unique_lock<std::mutex> ulk(mtx);//加互斥锁

    //之所以用一个while循环而不是if，是因为存在虚假唤醒情景。
    //当notify激活了多个线程之后，如果某个线程率先拿到锁将数据取空，
    //其他线程应该再次检查一下数据是否为空。
    while (nList.size() == MAXBUFF) //当前缓冲区已满
    {
        std::cout << "缓冲区已满，生产者进入等待状态..." << endl;
        //消费者等待"产品库缓冲区不为空"这一条件发生
        producervar.wait(ulk); //等待消费者取走数据，并释放锁
    }

    std::cout << "producer->id：" << this_thread::get_id() << endl;

    std::cout << "生产者生产数据：" << ndata << endl;
    nList.push_back(ndata);
    ++ndata;
    std::cout << "缓冲区大小：" << nList.size() << endl;

    if (!nList.empty())
    {
        consumervar.notify_all(); //该线程调用notify_*唤醒被wait()的线程，wait()自动调用lock()
    }
    ulk.unlock();
}

//消费者线程
void ConsumerThread()
{
    sleep(1);

    std::unique_lock<std::mutex> ulk(mtx);

    while (nList.empty())
    {
        std::cout << "缓冲区为空，消费者进入等待状态..." << endl;
        //生产者等待"产品库缓冲区不为满"这一条件发生
        consumervar.wait(ulk); //当前线程调用wait()后将被阻塞，函数自动调用unlock()释放锁，进入等待状态
    }

    std::cout << "consumer->id：" << this_thread::get_id() << endl;

    std::cout << "消费者取走数据：" << nList.front() << endl;
    nList.pop_front();
    std::cout << "缓冲区大小：" << nList.size() << endl;

    if (nList.size() != MAXBUFF)
    {
        producervar.notify_all(); //唤醒生产者线程进入生产
    }
    ulk.unlock();
}

//生产者任务
void ProducerTask()
{
    for(int i=0;i<PRODUCEDATA;++i)
    {
        ProducerThread(i);
    }
}

//消费者任务
void ConsumerTask()
{
    static int ncount=0;
    while(1)
    {
        sleep(1);

        ConsumerThread();
        if(++ncount == PRODUCEDATA) break;
    }
}

void ThreadTest()
{
    std::thread thd(FunThread);
    thd.join();

    CppDemo::Start();

    cout<<"Main Function Thread"<<endl;

    std::thread addth(Add,2);
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::thread multh(Multi,10);

    addth.detach();
    multh.detach();//有可能只有一个执行输出，有可能一个函数执行2次输出

    // addth.join();
    // multh.join();
    //Add data = 3
    //Multi data = 30

    auto lafun=[](int a)
    {
        int b=100;
        cout<<"Lambda表达式--detach："<<a*b<<endl;
    };

    std::thread lamth(lafun,5);
    lamth.detach();
}

void ProdConSTest()
{
    std::thread producerth(ProducerTask);
    std::thread consumerth(ConsumerTask);
    producerth.join();
    consumerth.join();
}

//<-------------------future-promise------------------>

void FutureThread(const std::vector<int> &vctBig, std::promise<double> prom)
{
    double sum = std::accumulate(vctBig.begin(),vctBig.end(),0.0);
    double avg=0.0;
    if(!vctBig.empty())
    {
        avg = sum / vctBig.size();
    }
    prom.set_value(avg);//提醒future
}

//使用promise在线程间传递结果
void FutureTask()
{
    //std::future是一次性的。
    //std::promise只能调用一次get_future，std::future也只能调用一次get()。
    //如果想在多个线程中共享一个std::promise的设置值，可以使用std::shared_future。
    
    //每个std::promise对象都有个对应的std::future对象，其他人可以通过它来获取promise设置的值。
    std::promise<double> prom;
    std::future<double> fu = prom.get_future();//取回结果

    std::vector<int> vct = {1,2,3,4,5,6};
    //以右值引用的方式进行传递，本线程中的prom对象转移给子线程，保证主线程不会一直阻塞
    std::thread thd(FutureThread,std::ref(vct),std::move(prom));
    thd.detach();

    double avg = fu.get();//阻塞一直到子线程调用set_value

    std::cout<<"avg = "<<avg<<endl;
}

//<-------------------原子操作------------------>
//long sum=0L;
std::atomic_long sum = {0L};//不要写成std::atomic_long sum = 0L的形式，因为long类型是不可以隐式转换为std::atomic_long类型的。

void SumFun()
{
    for(int i=1;i<100000;++i)
    {
        sum+=i;
    }
}

void SumFunTest()
{
    //SumFun();
    std::cout<<"Before join sum = "<<sum<<endl;
    std::thread th1(SumFun);
    std::thread th2(SumFun);

    th1.join();
    th2.join();
    std::cout<<"After  join sum = "<<sum<<endl;
}

//int a = 1;
std::atomic_int a {1};

void fun()
{
    while(true)
    {
        //mtx.lock();
        std::cout<<a<<endl;
        a++;
        //mtx.unlock();
        if(a>10) break;
    }
}

void funTest()
{
    std::thread th1(fun);
    //std::thread th2(fun);

    th1.join();
    //th2.join();
}

// int main()
// {
//     std::cout<<"<----------------------------->"<<endl;
    
//     funTest();

//     system("pause");
//     return 0;
// }

/** MAXBUFF=5 结果--有无ulk.unlock()都一样
producer->id：140716713309952
生产者生产数据：0
缓冲区大小：1
consumer->id：140716704917248
消费者取走数据：0
缓冲区大小：0
producer->id：140716713309952
生产者生产数据：1
缓冲区大小：1
producer->id：140716713309952
生产者生产数据：2
缓冲区大小：2
consumer->id：140716704917248
消费者取走数据：1
缓冲区大小：1
producer->id：140716713309952
生产者生产数据：3
缓冲区大小：2
producer->id：140716713309952
生产者生产数据：4
缓冲区大小：3
consumer->id：140716704917248
消费者取走数据：2
缓冲区大小：2
consumer->id：140716704917248
消费者取走数据：3
缓冲区大小：1
consumer->id：140716704917248
消费者取走数据：4
缓冲区大小：0
*/

/**没有sleep(1)的结果：生产者和消费者按各自顺序执行，没有了同步的概念
producer->id：140188509210368
生产者生产数据：0
缓冲区大小：1
producer->id：140188509210368
生产者生产数据：1
缓冲区大小：2
producer->id：140188509210368
生产者生产数据：2
缓冲区大小：3
producer->id：140188509210368
生产者生产数据：3
缓冲区大小：4
producer->id：140188509210368
生产者生产数据：4
缓冲区大小：5
consumer->id：140188500817664
消费者取走数据：0
缓冲区大小：4
consumer->id：140188500817664
消费者取走数据：1
缓冲区大小：3
consumer->id：140188500817664
消费者取走数据：2
缓冲区大小：2
consumer->id：140188500817664
消费者取走数据：3
缓冲区大小：1
consumer->id：140188500817664
消费者取走数据：4
缓冲区大小：0
缓冲区为空，消费者进入等待状态...
*/

/** 生产数量>缓冲区数量时，条件满足时各自while语句生效
producer->id：140612926564096
生产者生产数据：0
缓冲区大小：1
consumer->id：140612918171392
消费者取走数据：0
缓冲区大小：0
producer->id：140612926564096
生产者生产数据：1
缓冲区大小：1
producer->id：140612926564096
生产者生产数据：2
缓冲区大小：2
consumer->id：140612918171392
消费者取走数据：1
缓冲区大小：1
producer->id：140612926564096
生产者生产数据：3
缓冲区大小：2
producer->id：140612926564096
生产者生产数据：4
缓冲区大小：3
consumer->id：140612918171392
消费者取走数据：2
缓冲区大小：2
producer->id：140612926564096
生产者生产数据：5
缓冲区大小：3
producer->id：140612926564096
生产者生产数据：6
缓冲区大小：4
consumer->id：140612918171392
消费者取走数据：3
缓冲区大小：3
producer->id：140612926564096
生产者生产数据：7
缓冲区大小：4
producer->id：140612926564096
生产者生产数据：8
缓冲区大小：5
consumer->id：140612918171392
消费者取走数据：4
缓冲区大小：4
producer->id：140612926564096
生产者生产数据：9
缓冲区大小：5
缓冲区已满，生产者进入等待状态...
consumer->id：140612918171392
消费者取走数据：5
缓冲区大小：4
producer->id：140612926564096
生产者生产数据：10
缓冲区大小：5
缓冲区已满，生产者进入等待状态...
consumer->id：140612918171392
消费者取走数据：6
缓冲区大小：4
producer->id：140612926564096
生产者生产数据：11
缓冲区大小：5
缓冲区已满，生产者进入等待状态...
consumer->id：140612918171392
消费者取走数据：7
缓冲区大小：4
producer->id：140612926564096
生产者生产数据：12
缓冲区大小：5
缓冲区已满，生产者进入等待状态...
consumer->id：140612918171392
消费者取走数据：8
缓冲区大小：4
producer->id：140612926564096
生产者生产数据：13
缓冲区大小：5
缓冲区已满，生产者进入等待状态...
consumer->id：140612918171392
消费者取走数据：9
缓冲区大小：4
producer->id：140612926564096
生产者生产数据：14
缓冲区大小：5
缓冲区已满，生产者进入等待状态...
consumer->id：140612918171392
消费者取走数据：10
缓冲区大小：4
producer->id：140612926564096
生产者生产数据：15
缓冲区大小：5
缓冲区已满，生产者进入等待状态...
consumer->id：140612918171392
消费者取走数据：11
缓冲区大小：4
producer->id：140612926564096
生产者生产数据：16
缓冲区大小：5
缓冲区已满，生产者进入等待状态...
consumer->id：140612918171392
消费者取走数据：12
缓冲区大小：4
producer->id：140612926564096
生产者生产数据：17
缓冲区大小：5
缓冲区已满，生产者进入等待状态...
consumer->id：140612918171392
消费者取走数据：13
缓冲区大小：4
producer->id：140612926564096
生产者生产数据：18
缓冲区大小：5
缓冲区已满，生产者进入等待状态...
consumer->id：140612918171392
消费者取走数据：14
缓冲区大小：4
producer->id：140612926564096
生产者生产数据：19
缓冲区大小：5
consumer->id：140612918171392
消费者取走数据：15
缓冲区大小：4
consumer->id：140612918171392
消费者取走数据：16
缓冲区大小：3
consumer->id：140612918171392
消费者取走数据：17
缓冲区大小：2
consumer->id：140612918171392
消费者取走数据：18
缓冲区大小：1
consumer->id：140612918171392
消费者取走数据：19
缓冲区大小：0 
 */